#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <string>
#include <cstring>
#include <cstdio>
#include<cassert>
#include <memory>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include "Log.hpp"
#include"ThreadPool.hpp"
#include"Task.hpp"
#include"Thread.hpp"
#include<thread>
#define SIZE 1024

// struct ThreadData
// {
//     uint16_t _port;
//     std::string _ip;
//     int _sock;
// }
// static void Service(int sock,const std::string& ip,const int16_t& port)
// {
//     char buffer[SIZE];
//     while(true)
//     {
//         //因为TCP面向字节流，所以read和write可以直接使用
//         ssize_t s = read(sock,buffer,sizeof(buffer)-1);
//         if(s>0)//读取成功
//         {
//             buffer[s]=0;
//             std::cout<<ip<<" : "<< port <<"# "<<buffer<<std::endl;
//         }
//         else if(s==0)//连接关闭
//         {
//             logMessage(NORMAL, "%s:%d shutdown, me too!", ip.c_str(), port);
//             break;
//         }
//         else{ // 读取失败
//             logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));
//             break;
//         }
//         //服务端处理并写回
//         std::string message = "server->";
//         message+=buffer;
//         write(sock,message.c_str(),message.size());   
//     }
// }
static void Service_routine(int sock,const std::string& ip,const int16_t& port,const std::string& name)
{
    char buffer[SIZE];
    //长连接，只要对面客户端不退出，服务端的线程就会一直处理，即便对面没有发送数据
    while(true)
    {
        //因为TCP面向字节流，所以read和write可以直接使用
        ssize_t s = read(sock,buffer,sizeof(buffer)-1);
        if(s>0)//读取成功
        {
            buffer[s]=0;
            std::cout<<name<<"|"<<ip<<" : "<< port <<"# "<<buffer<<std::endl;
        }
        else if(s==0)//连接关闭
        {
            logMessage(NORMAL, "%s:%d shutdown, me too!", ip.c_str(), port);
            break;
        }
        else{ // 读取失败
            logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));
            break;
        }
        //服务端处理并写回
        std::string message = "server->";
        message+=buffer;
        write(sock,message.c_str(),message.size());   
    }
}
static void change(int sock, const std::string &clientip,
                   const uint16_t &clientport, const std::string &thread_name)
{
    //短链接
    char buffer[SIZE];
    // read && write 可以直接被使用！
    ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
    if (s > 0)
    {
        buffer[s] = 0; // 将发过来的数据当做字符串
        std::cout << thread_name << "|" << clientip << ":" << clientport << "# " << buffer << std::endl;
        std::string message;
        char *start = buffer;
        while(*start)
        {
            char c;
            if(islower(*start)) c = toupper(*start);
            else c = *start;
            message.push_back(c);
            start++;
        }

        write(sock, message.c_str(), message.size());
    }
    else if (s == 0) // 对端关闭连接
    {
        logMessage(NORMAL, "%s:%d shutdown, me too!", clientip.c_str(), clientport);
    }
    else
    { //
        logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));
    }
    close(sock);
}
class TcpServer
{
    const static int gbacklog = 20;
    // static void thread_rount(ThreadData* td)
    // {
    //     Service(td->_sock,td->_ip,td->_port);
    // }
public:
    TcpServer(uint16_t port, std::string ip = "") : sock_(-1), port_(port), ip_(ip),threadpool_(ThreadPool<Task>::GetOnceTP())
    {
    }
    void initServer()
    {
        // 1.创建套接字
        sock_ = socket(AF_INET, SOCK_STREAM, 0); // SOCK_STREAM表示TCP服务
        if (sock_ < 0)
        {
            logMessage(FATAL, "create socket fail,%d:%s", errno, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "creat socket success,%d", sock_);
        // 2.bind
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        local.sin_addr.s_addr = ip_.empty() ? INADDR_ANY : inet_addr(ip_.c_str());//任意地址绑定
        if (bind(sock_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "bind fail,%d:%s", errno, strerror(errno));
            exit(3);
        }
        // 3.TCP是面向连接的，所以我们需要先进行连接
        if (listen(sock_, gbacklog) < 0)
        {
            logMessage(FATAL, "listen error,%d:%s", errno, strerror(errno));
            exit(4);
        }
        logMessage(NORMAL, "init server success");
    }
    void Start()
    {
        signal(SIGCHLD, SIG_IGN);//设置子进程执行完毕后自动销毁
        threadpool_->run();
        while(true)
        {
            //4.获取连接
            struct sockaddr_in src;
            socklen_t len = sizeof(src);
            int service_sock = accept(sock_,(struct sockaddr *)&src,&len);//返回提供服务的套接字
            //没有链接的时候阻塞等待
            if(service_sock < 0)//连接失败
            {
                logMessage(ERROR, "accept error, %d:%s", errno, strerror(errno));
                continue;
            }

            uint16_t client_port = ntohs(src.sin_port);
            std::string client_ip = inet_ntoa(src.sin_addr);
            logMessage(NORMAL, "link success, servicesock: %d | %s : %d |\n",\
                service_sock, client_ip.c_str(), client_port);
            //(1)多进程提供服务
            // pid_t id = fork();
            // assert(id!=-1);
            // if(id == 0)
            // {
            //     //子进程
            //     close(sock_);//关闭监听的套接字
            //     Service(service_sock,client_ip,client_port);
            //     exit(0);
            // }
            // close(service_sock);//父进程关闭不会影响子进程，子进程写时拷贝
            //父进程必须要关闭，不然父进程可使用的文件描述符越来越少

            //(2)多进程提供服务进阶版
            // pid_t id = fork();
            // assert(id!=-1);
            // if(id == 0)
            // {
            //     //子进程
            //     close(sock_);//关闭监听的套接字
            //     if(fock()>0) exit(0);//子进程创建孙子进程去执行，子进程直接退出，孙子进程被OS 领养，OS回收孙子进程
            //     //下面是孙子进程执行
            //     Service(service_sock,client_ip,client_port);
            //     exit(0);
            //     //
            // }
            // waitpid(id,nullptr,0);//父进程不会阻塞
            // close(service_sock);

            //(3)多线程版本
            //为什么使用指针而不临时对象？原因在于临时对象存在线程安全，有可能多线程还没调用，其他的数据就将临时对象进行覆盖
            //但是指针并不会，每次都是在堆上申请的空间
            // ThreadData* td = new ThreadDate();
            // td->_ip=client_ip;
            // td->_port = client_port;
            // td->_sock = service_sock;
            // std::thread sev(thread_rount,td);
            // sev.detach();//线程分离

            //(4)线程池版本

            Task t(service_sock,client_ip,client_port,change);
            threadpool_->PushTask(t);
        }
    }
    ~TcpServer()
    {
    }

private:
    uint16_t port_;  // 端口号
    std::string ip_; // ip
    int sock_;       // 套接字--只获得连接
    std::unique_ptr<ThreadPool<Task>> threadpool_;
};
